﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;

namespace library
{
    public class ByteArrayComparer : IEqualityComparer<byte[]>
    {
        public bool Equals(byte[] left, byte[] right)
        {
            if (left == null || right == null)
            {
                return left == right;
            }
            return left.SequenceEqual(right);
        }
        public int GetHashCode(byte[] key)
        {
            if (key == null)
                throw new ArgumentNullException("key");
            return key.Sum(b => b);
        }
    } 

    static class LocalPosts
    {
        static List<p2pPost> posts = new List<p2pPost>();

        static Dictionary<byte[], p2pPost> postsByAddress = new Dictionary<byte[], p2pPost>(new ByteArrayComparer());

        static Dictionary<byte[], List<p2pPost>> postsByTargetAddress = new Dictionary<byte[], List<p2pPost>>(new ByteArrayComparer());

        static Dictionary<byte[], List<p2pPost>> postsByValueAddress = new Dictionary<byte[], List<p2pPost>>(new ByteArrayComparer());

        internal static void Add(p2pPost post)
        {
            lock (posts)
                posts.Add(post);

            lock(postsByAddress)
                postsByAddress.Add(post.Address, post);

            if (post.targetAddress != null)
            {
                lock (postsByTargetAddress)
                    if (postsByTargetAddress.ContainsKey(post.targetAddress))
                        postsByTargetAddress[post.targetAddress].Add(post);
                    else
                        postsByTargetAddress.Add(post.targetAddress, new List<p2pPost>(new p2pPost[] { post }));
            }

            var value = Utils.FromBase64String(post.Content);

            if (value != null)
            {
                lock (postsByValueAddress)
                    if (postsByValueAddress.ContainsKey(value))
                        postsByValueAddress[value].Add(post);
                    else
                        postsByValueAddress.Add(value, new List<p2pPost>(new p2pPost[] { post }));
            }

            LocalPackets.Add(post.Address, post.ToBytes(), Client.LocalPeer);
        }

        internal static ValueHits<p2pPost> SearchByValue(string value)
        {
            ValueHits<p2pPost> result = new ValueHits<p2pPost>();
            lock (posts)
                posts.ForEach(x =>
                {
                    if (x.Content != null && x.Content.Contains(value))
                    {
                        result.Add(x);
                    }
                });

            return result;
        }

        internal static ValueHits<p2pPost> Search(byte[] address)
        {
            ValueHits<p2pPost> result = new ValueHits<p2pPost>();

            lock(postsByAddress)
            {
                if (postsByAddress.ContainsKey(address))
                    result.Add(postsByAddress[address]);
            }

            lock(postsByTargetAddress)
            {
                if (postsByTargetAddress.ContainsKey(address))
                    result.AddRange(postsByTargetAddress[address]);
            }

            lock (postsByValueAddress)
            {
                if (postsByValueAddress.ContainsKey(address))
                    result.AddRange(postsByValueAddress[address]);
            }

            //lock (posts)
            //    posts.ForEach(x =>
            //    {
            //        if ((x.targetAddress != null
            //                && Addresses.Equals(x.targetAddress, address)) ||

            //            (Addresses.Equals(x.Address, address)) ||

            //            (!string.IsNullOrEmpty(x.Content) &&
            //                x.Content.Length == Parameters.base64AddressSize &&
            //                Addresses.Equals(Utils.FromBase64String(x.Content), address)))
            //        {
            //            if (!LocalIndex.SearchByTargetAddress(x.Address).Any())
            //            {
            //                //todo: post recebido, indexar
            //                //if(!string.IsNullOrEmpty(x.ValueText))
            //                //    LocalIndex.ParseContent(x.Address, x.ValueText);
            //            }

            //            result.Add(x);
            //        }
            //    });

            return result;
        }

        internal static p2pPost Get(byte[] address)
        {
            lock (posts)
                return posts.FirstOrDefault(x => Addresses.Equals(x.Address, address));
        }

        internal static void Load()
        {
            if (File.Exists(Parameters.localPostsFile))
            {
                var buffer = File.ReadAllBytes(Parameters.localPostsFile);

                var offset = 0;

                while (offset < buffer.Length)
                {
                    var address = Utils.ReadBytes(buffer, offset);

                    offset += Parameters.addressSize;

                    var packet = LocalPackets.Get(address);

                    if (packet != null)
                    {
                        var post = p2pPost.FromBytes(address, packet);

                        if (post != null)
                            Add(post);
                    }
                }
            }
        }

        internal static void Save()
        {
            List<byte> buffer = new List<byte>();

            lock (posts)
                foreach (var post in posts)
                    buffer.AddRange(post.Address);

            File.WriteAllBytes(Parameters.localPostsFile, buffer.ToArray());
        }
    }
}
